package com.hykc.auth.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.aopuxun.auth.config.JwtProperties;
import com.aopuxun.auth.service.AuthService;
import com.aopuxun.common.CookieUtils;
import com.aopuxun.common.Encrypt;
import com.aopuxun.common.JsonUtils;
import com.aopuxun.common.auth.entity.Payload;
import com.aopuxun.common.auth.entity.UserInfo;
import com.aopuxun.common.auth.utils.JwtUtils;
import com.aopuxun.common.enums.ExceptionEnum;
import com.aopuxun.common.exceptions.DdException;
import com.aopuxun.common.threadlocals.UserHolder;
import com.aopuxun.system.SysUserDTO;
import com.aopuxun.system.client.SystemClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.aopuxun.common.constants.RocketMQConstants.TAGS.VERIFY_CODE_TAGS;
import static com.aopuxun.common.constants.RocketMQConstants.TOPIC.SMS_TOPIC_NAME;

@Service
@Slf4j
public class AuthServiceImpl implements AuthService {

    @Autowired
    private SystemClient systemClient;
    @Autowired
    private JwtProperties prop;
    @Autowired
    private StringRedisTemplate redisTemplate;

    private String prefix = "dd:user:verify:phone:";

    @Autowired
    private RocketMQTemplate rocketMQTemplate;


    @Override
    public Map<String,Object> login(String telephone, String password, HttpServletResponse response) {
        SysUserDTO sysUserDTO = systemClient.getUserByPhone(telephone);

        String dbPWD = sysUserDTO.getPassword();
        if (dbPWD == null){
            Map<String,String> returnMap = new HashMap<>();
            returnMap.put("phone",telephone);
            throw new DdException(410,JsonUtils.toString(returnMap));
        } else {
            String inputPWD = Encrypt.encodeByMD5(password);
            if (!inputPWD.equals(dbPWD)){
                throw new DdException(400,"密码错误！");
            }
        }
        try {
            generateCookie(sysUserDTO, response);

            Map<String,Object> result = new HashMap<>();
            result.put("userId",sysUserDTO.getId());
            result.put("userName",sysUserDTO.getUsername());
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            throw new DdException(ExceptionEnum.INVALID_USERNAME_PASSWORD);
        }
    }

    @Override
    public UserInfo verifyUser(HttpServletRequest request, HttpServletResponse response) {
        try {
            String token = CookieUtils.getCookieValue(request, prop.getUser().getCookieName());
            Payload<UserInfo> payload = JwtUtils.getInfoFromToken(token, prop.getPublicKey(), UserInfo.class);

            String tokenId = payload.getId();
            Boolean b = redisTemplate.hasKey(tokenId);
            if (b != null && b) {
                throw new DdException(ExceptionEnum.UNAUTHORIZED);
            }

            Date expiration = payload.getExpiration();
            // 获取刷新时间
            DateTime refreshTime = new DateTime(expiration.getTime()).minusMinutes(prop.getUser().getMinRefreshInterval());
            // 判断是否已经过了刷新时间
            if (refreshTime.isBefore(System.currentTimeMillis())) {
                // 如果过了刷新时间，则生成新token
                token = JwtUtils.generateTokenExpireInMinutes(payload.getUserInfo(), prop.getPrivateKey(), prop.getUser().getExpire());
                // 写入cookie
                CookieUtils.newCookieBuilder()
                        // response,用于写cookie
                        .response(response)
                        // 保证安全防止XSS攻击，不允许JS操作cookie
                        .httpOnly(true)
                        // 设置domain
                        .domain(prop.getUser().getCookieDomain())
                        // 设置cookie名称和值
                        .name(prop.getUser().getCookieName()).value(token)
                        // 写cookie
                        .build();
            }
            return payload.getUserInfo();
        } catch (Exception e) {
            e.printStackTrace();
            throw new DdException(ExceptionEnum.UNAUTHORIZED);
        }
    }

    @Override
    public void logout(HttpServletRequest request, HttpServletResponse response) {
        try {
            String token = CookieUtils.getCookieValue(request, prop.getUser().getCookieName());
            Payload<UserInfo> payload = JwtUtils.getInfoFromToken(token, prop.getPublicKey(), UserInfo.class);
            String id = payload.getId();
            Date expiration = payload.getExpiration();
            long timeout = expiration.getTime() - System.currentTimeMillis();
            redisTemplate.opsForValue().set(id, "1", timeout, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            e.printStackTrace();
            throw new DdException(ExceptionEnum.UNAUTHORIZED);

        } finally {
            CookieUtils.deleteCookie(prop.getUser().getCookieName(), prop.getUser().getCookieDomain(), response);
        }
    }

    @Override
    public Map<String,Object> checkCodeLogin(JSONObject jsonObject, HttpServletResponse response) {
        String telephone = jsonObject.getString("phone");
        String checkCode = jsonObject.getString("checkCode");
        String cacheCode = redisTemplate.opsForValue().get("dd:user:verify:phone:" + telephone);
        if (StringUtils.isBlank(cacheCode) || !checkCode.equals(cacheCode)) {
            throw new DdException(ExceptionEnum.INVALID_VERIFY_CODE);
        }

        SysUserDTO sysUserDTO = systemClient.getUserByPhone(telephone);
        if (sysUserDTO.getPassword() == null){
            throw new DdException(409,jsonObject.toJSONString());
        }
        try {
            generateCookie(sysUserDTO, response);

            Map<String,Object> result = new HashMap<>();
            result.put("userId",sysUserDTO.getId());
            result.put("userName",sysUserDTO.getUsername());
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            throw new DdException(ExceptionEnum.INVALID_VERIFY_CODE);
        }
    }




    /**
     * 发送短信验证码
     * 生成随机的验证码
     * 把验证码放入redis，并设置有效期
     * 把验证码和手机号发送到 消息中间件
     *
     * @param telephone
     * @return
     */
    @Override
    public void sendCode(String telephone) {

//        生成4位随机验证码
        String code = RandomStringUtils.randomNumeric(6);
        //String code = "1234";
//        把验证码放入redis,string结构，key- 手机号 ，val -验证码
//        一般在redis中，设置key需要包含前缀
        redisTemplate.opsForValue().set(prefix + telephone, code, 5, TimeUnit.MINUTES);

        String dest = SMS_TOPIC_NAME + ":" + VERIFY_CODE_TAGS;
        Map<String, String> msg = new HashMap<>();
        msg.put("telephone", telephone);
        msg.put("code", code);
        log.info("发送的消息内容：{}", msg);
//        发送消息
        rocketMQTemplate.convertAndSend(dest, JsonUtils.toString(msg));
    }
    @Override
    public void resetPassword(String telephone, String checkCode, String password) {

        String cacheCode = redisTemplate.opsForValue().get(prefix + telephone);
        if (StringUtils.isBlank(cacheCode) || !checkCode.equals(cacheCode)) {
            throw new DdException(ExceptionEnum.INVALID_VERIFY_CODE);
        }
        SysUserDTO sysUserDTO = systemClient.getUserByPhone(telephone);
        String md5Password = Encrypt.encodeByMD5(password);
        sysUserDTO.setPassword(md5Password);

        systemClient.updateUser(sysUserDTO);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changePassword(String oldPassword, String newPassword, HttpServletRequest request, HttpServletResponse response) {
        Integer userId = UserHolder.get();
        SysUserDTO sysUserDTO = systemClient.getUser(userId);
        String telephone = sysUserDTO.getPhone();
        SysUserDTO result = systemClient.queryUser(telephone, oldPassword);
        if (result ==null){
            throw new DdException(500,"原密码不正确，请重新输入");
        }
        result.setPassword(Encrypt.encodeByMD5(newPassword));
        systemClient.changePassword(result);

        this.logout(request,response);
    }

    private void generateCookie(SysUserDTO sysUserDTO, HttpServletResponse response) {
        UserInfo userInfo = new UserInfo(sysUserDTO.getId(), sysUserDTO.getUsername(),sysUserDTO.getRoleId(), sysUserDTO.getRoleName());
        String token = JwtUtils.generateTokenExpireInMinutes(userInfo, prop.getPrivateKey(), prop.getUser().getExpire());
        System.out.println("token:" + token);
        CookieUtils.newCookieBuilder()
                .name(prop.getUser().getCookieName())
                .domain(prop.getUser().getCookieDomain())
                .httpOnly(true)
                .value(token)
                .response(response).build();
    }
}
